-module( clientGate ).

-export( [ start/2 ] ).

-define( CL_UINT32, 32/unsigned-big-integer ).
-define( CL_INT32, 32/signed-big-integer ).
-define( CL_UINT8, 8/unsigned-big-integer ).
-define( CL_INT8, 8/signed-big-integer ).
-define( CL_FLOAT, 32/float ).

-define( PktId_Sv_LoginResult, 100 ).
-define( PktId_Sv_ObjPos, 101 ).
    
-define( PktId_Cl_Login,    200 ).
-define( PktId_Cl_Pos,      201 ).
-define( PktId_Cl_Attack,   202 ).

start( LoginNewUserFun, Port ) -> 
  spawn( fun() -> entryFunction( LoginNewUserFun, Port ) end ).
  
  
entryFunction( LoginNewUserFun, Port ) ->
  { ok, Listen } = gen_tcp:listen( Port, [ binary, { packet, 4 }, { reuseaddr, true }, { active, true } ] ),
  acceptConnections( Listen, LoginNewUserFun ).



acceptConnections( Listen, LoginNewUserFun ) ->
  error_logger:info_msg( "Accepting new connections: ~p~n", [Listen] ),
  AcceptResult = gen_tcp:accept( Listen ),
  error_logger:info_msg( "Accept result: ~p~n", [AcceptResult] ),
  { ok, Sock } = AcceptResult,
  
  Pid = spawn( fun() -> acceptConnections( Listen, LoginNewUserFun ) end ),
  gen_tcp:controlling_process( Listen, Pid ), %%FIXME: race condition, but otherwise listen socket will be closed when this process exits
  
  work( Sock, LoginNewUserFun ).


  
work( Sock, LoginNewUserFun ) ->
  error_logger:info_msg( "~p: Working with client ~p\n", [ Sock, inet:peername( Sock ) ] ),
  { ControlPid, ClientId } = performLogin( Sock, LoginNewUserFun ),
  pumpPackets( ControlPid, Sock, ClientId ).


 
getPktFromClient( Sock ) ->
  receive
    { tcp, Sock, Bin } ->
      error_logger:info_msg( "Received binary = ~p\n", [ Bin ] ),
      << PktId:?CL_UINT32, Body/binary >> = Bin,
      error_logger:info_msg( "Got pkt: ~p ~p\n", [ PktId, Body ] ),
      { PktId, Body };

    { tcp_closed, Sock } ->
      error_logger:info_msg( "Socket closed~n" ),
      {}
  end.


  
performLogin( Sock, LoginNewUserFun ) ->
  error_logger:info_msg( "Performing login...\n" ),
  {PktId, LoginBin} = getPktFromClient( Sock ),
  error_logger:info_msg( "Got login packet: ~p ~p\n", [ PktId, LoginBin ] ),
  PktId = ?PktId_Cl_Login,
  case LoginNewUserFun( binary_to_list( LoginBin ) ) of
    { login_ok, ControlPid, ClientId } ->
      error_logger:info_msg( "Login OK, sending to client: clienid ~p, address ~p\n", [ ClientId, inet:peername( Sock ) ] ),
      gen_tcp:send( Sock, <<?PktId_Sv_LoginResult:?CL_UINT32, ClientId:?CL_UINT32>>),
      { ControlPid, ClientId };
    { login_refused, _Reason } -> exit( login_refused )
  end.
  



pumpPackets( ControlPid, Sock, ClientId ) ->
  Msg =  decodePacket( ControlPid, getPktFromClient( Sock ) ),
  Sock, Msg, %%TEMP: Use variables
  sendOutgoingPackets( Sock ),
  pumpPackets( ControlPid, Sock, ClientId ).



sendOutgoingPackets( Sock ) ->
  %%TODO: Pick messages from process queue and write them to socket
  Sock.



decodePacket( ControlPid, { ?PktId_Cl_Pos, Body } ) ->
  << X:?CL_FLOAT, Y:?CL_FLOAT, Z:?CL_FLOAT >> = Body,
  %%error_logger:info_msg( "Got client pos: ~p", [ { X, Y, Z } ] ),
  ControlPid ! { client_pos, { X, Y, Z } };

decodePacket( ControlPid, { ?PktId_Cl_Attack, Body } ) ->
  ControlPid, Body.
